package lib;

import haxe.extern.Rest;
import lib.Ffi;

class Lua {
	public static var path = untyped __lua__('__dirname');

	/**
	 * 执行lua代码
	 * @param code 代码
	 * @return Dynamic 返回结果
	 */
	public static var eval:String->Dynamic = untyped __lua__('eval');

	/**
	 * 设置元表
	 * @param a 表
	 * @param b 元表
	 * @param 访问表 `__index(k: string, v: string)`
	 * @param 写入表 `__newindex (k: string, v: string)`
	 * @param 写入表 `__add (k: string)`,
	 * @param 写入表 `__sub (k: string)`
	 * @param 写入表 `__mul (k: string)`
	 * @param 写入表 `__div (k: string)`
	 * @param 写入表 `__mod (k: string)`
	 * @param 写入表 `__unm (k: string)`
	 * @param 写入表 `__concat (k: string)`
	 * @param 写入表 `__eq (k: string)`
	 * @param 写入表 `__lt (k: string)`
	 * @param 写入表 `__le (k: string)`
	 * @param 写入表 `__call (k: string)`
	 * @param 写入表 `__tostring (k: string)`
	 */
	public static var setmetatable:Dynamic->Dynamic->Dynamic = untyped __lua__('setmetatable');

	/**
	 * 内核io库
	 */
	public static var io = Io;

	/**
	 * 内核io库
	 */
	public static var string = LuaString;

	/**
	 * 延时
	 * @param ms 毫秒
	 */
	public static function sleep(ms:Int):Void {};

	/**
	 * 对table表进行原始写入。不触发元表 __newindex
	 * @param a 表
	 * @param b 键
	 * @param c 值
	 */
	public static var rawset:{} -> Dynamic -> Dynamic -> Void = untyped __lua__('rawset');

	/**
	 * 对table表进行原始访问。不触发元表 __newindex
	 * @param a 表
	 * @param b 键
	 */
	public static var rawget:{} -> Dynamic -> Dynamic = untyped __lua__('rawget');

	/**
	 * 抛出错误并结束程序 错误码1
	 * @param a 信息
	 *
	 */
	public static var error:String->?Int->Void = untyped __lua__('error');

	/**
	 * 引入包
	 * @param a 包名
	 */
	public static var require:String->Dynamic = untyped __lua__('require');

	/**
	 * 启动命令行
	 */
	public static var _CMDLINE = (function():String {
		Ffi.cdef("const char* GetCommandLineA()");
		var func = Ffi.getDef("GetCommandLineA");
		return Ffi.string(func());
	})();

	/**
	 * Luajit版本号
	 */
	public static var _VERSION:String = untyped __lua__('_VERSION');

	/**
	 * 返回类型
	 * @param a 变量
	 */
	public static var typeOf:Dynamic->TYPE = untyped __lua__('type');

	public static var _type:TYPE;

	public static function codeInfo(?pos:haxe.PosInfos):haxe.PosInfos {
		return pos;
	}

	/**
	 * 定义多参数函数
	 */
	@:overload(function(f:Array<Dynamic>->Void):Dynamic {})
	public static function _f(f:Array<Dynamic>->Dynamic):Dynamic {
		return untyped __lua__("function(...)
			return f( {...} )
		end");
	}

	// public static function getCmdLine() {}
}

@:native("string")
extern class LuaString {
	public static function gmatch(s:String, pattern:String):Void;

	/**
	 * 格式化文本
	 * @param str 内容
	 * @param arg 不定参数
	 * @return 返回格式化后的文本
	 */
	public static function format(str:String, arg:Rest<Dynamic>):String;

	static inline function randStr(len:Int = 16):String {
		var buff = new Buff(len + 2);
		for (i in 0...len)
			buff[i] = 65 + Math.floor(Math.random() * 25);
		return buff.toString();
	}
}

@:native("table")
extern class Table {}

class Io {
	/**
	 * 打印变量
	 * 如果要打印变量位置 和数组,hashLuaTable,Map等等内部数据请用`trace(...)`
	 * 区别于`trace` print可以打印lua的CDDATA `trace`会无法识别报错
	 */
	public static var print:Rest<Dynamic>->Void = untyped __lua__('print');

	/**
	 * 打开文件
	 * @param a 文件地址
	 * @param b	模式
	 */
	public static var open:String->String->FileHandle = Io.open;

	public static function exist(fileName:String, ?status:Int):Void {};
}

@:native("io")
extern class Eio {
	/**
	 * 打开文件
	 * @param str
	 * @param mode
	 * @return Dynamic
	 */
	public static function open(str:String, mode:String):FileHandle;
}

extern class FileHandle {
	public function flush():Void;
	public function read(arg:Rest<Dynamic>):String;
	public function close():Void;
	public function write(str:String):Void;
	@:overload(function():Int {})
	@:overload(function(arg:String):Int {})
	public function seek(arg:String, pos:Int):Void;
}

@:native("os")
extern class Os {
	public static function execute(str:String):Dynamic;

	/**
	 * 退出程序
	 * @param code 退出码
	 */
	public static function exit(code:Int):Void;
}

/**
 * TYPE类型常量
 */
class TYPE {
	public static var string = "string";
	public static var number = "number";
	public static var bool = "bool";
}

@:callable
abstract ArgFunc(Dynamic) from Dynamic to Dynamic {
	inline public function new(func:Dynamic) {
		this = untyped __lua__("function(...)
			local a = {...}
			local b = {}
			local l = 0
			for k, v in pairs(a) do
				b[k-1] = v
				l = math.max(k,l)
			end
			return func(_hx_tab_array(b, l))
		end");
	}

	@:from
	static public function fromString(func:Dynamic) {
		return new ArgFunc(func);
	}
}

/**
 * 具有元表能力的原生luaTable
 */
abstract MetaTable(Dynamic) from Dynamic to Dynamic {
	inline public function new(tab:Dynamic) {
		this = {};
	}

	@:from static public function fromDynamic(tab:Dynamic) {
		return new MetaTable(tab);
	}

	@:op(A | B) public function add2(tab:Dynamic):MetaTable {
		return new MetaTable(tab);
	}

	@:op(A + B) public function add(tab:Dynamic):MetaTable {
		return new MetaTable(tab);
	}

	// @:op(a.b) public function fieldRead(name:String)
	// 	Reflect.callMethod
	// @:op(a.b) public function fieldWrite(name:String, value:Dynamic)
	// 	return this.split(name).join(value);
}

/**
 * 具有元表能力的原生luaTable
 */
@:arrayAccess
abstract Buff(Dynamic) from Dynamic to String {
	inline public function new(len:Int) {
		this = Ffi._new("char[?]", len);
	}

	@:to
	inline public function toString() {
		return Ffi.string(this);
	}
}
